package com.facemox.controller;

import java.io.IOException;
import java.nio.charset.Charset;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.TimeZone;
import java.util.UUID;
import javax.servlet.http.HttpServletRequest;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.NameValuePair;
import org.apache.http.auth.AuthScope;
import org.apache.http.auth.UsernamePasswordCredentials;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.CredentialsProvider;
import org.apache.http.client.HttpClient;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpDelete;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpPut;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.BasicCredentialsProvider;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.util.EntityUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.facemox.bean.EnvConfig;
import com.facemox.bean.FaceExecuteLog;
import com.facemox.bean.FaceInfo;
import com.facemox.bean.GlobalParameter;
import com.facemox.bean.PermissionAuthentication;
import com.facemox.bean.Statistic;
import com.facemox.service.AuthorizationService;
import com.facemox.service.EnvConfigService;
import com.facemox.service.FaceInfoService;
import com.facemox.service.GlobalParameterService;
import com.facemox.service.StatisticService;
import com.fasterxml.jackson.databind.ObjectMapper;

@RestController
public class FaceExecuteController {

    @Autowired
    private FaceInfoService faceInfoService;

    @Autowired
    private AuthorizationService authorizationService;

    @Autowired
    private StatisticService statisticService;

    @Autowired
    private Statistic statistic;

    @Autowired
    private FaceExecuteLog faceExecuteLog;

    @Autowired
    private GlobalParameterService globalParameterService;

    @Autowired
    private EnvConfigService envConfigService;

    /**
     * @MehtodDescription OAuth 2.0 客户端模式
     * @return 返回访问令牌：token_type access_token
     */
    public String permissionAuthentication(String requestTokenUrl, String username, String password)
            throws ClientProtocolException, IOException {

        CredentialsProvider provider = new BasicCredentialsProvider();
        UsernamePasswordCredentials credentials = new UsernamePasswordCredentials(username, password);
        provider.setCredentials(AuthScope.ANY, credentials);

        HttpClient client = HttpClientBuilder.create().setDefaultCredentialsProvider(provider).build();

        List<NameValuePair> nvps = new ArrayList<NameValuePair>();
        nvps.add(new BasicNameValuePair("grant_type", "client_credentials"));

        HttpPost post = new HttpPost(requestTokenUrl);
        post.setEntity(new UrlEncodedFormEntity(nvps));

        post.setHeader("Content-Type", "application/x-www-form-urlencoded");

        HttpResponse execute = client.execute(post);
        HttpEntity responseEntity = execute.getEntity();

        String strEntity = EntityUtils.toString(responseEntity);
        JSONObject parseObject = JSON.parseObject(strEntity);
        String access_token = parseObject.get("access_token").toString();
        String token_type = parseObject.get("token_type").toString();

        return token_type + " " + access_token;
    }

    /**
     * @module 执行接口测试
     */
    @RequestMapping("execute_face")
    public String execute_face(HttpServletRequest request)
            throws ClientProtocolException, InstantiationException, IllegalAccessException, IOException {

        String tempFID = request.getParameter("faceId");
        Integer faceId = Integer.valueOf(tempFID);

        FaceInfo face = faceInfoService.findFace(faceId);
        Integer authenticationId = face.getAuthenticationId();
        Integer moduleId = face.getModuleId();

        // 接口执行次数统计
        statistic.setFaceId(faceId);
        statistic.setModuleId(moduleId);
        statistic.setAuthenticationId(authenticationId);
        statisticService.addFaceStatisticItem(statistic);

        PermissionAuthentication auth = authorizationService.findAuthorizationByAuthenticationId(authenticationId);

        CloseableHttpResponse response = null;
        HttpEntity responseEntity = null;
        String responseBody = null;
        CloseableHttpClient httpClient = HttpClients.createDefault();
        
        switch (auth.getAuthenticationType()) {
        case "no_auth":

            String host0 = getEnvConfigHost(face);

            String body0 = getGlobalParameter(face);

            StringEntity entity0 = setHttpEntity(body0);

            switch (face.getFaceMethod()) {
            case "POST":
                HttpPost httpPost = new HttpPost("http://" + host0 + face.getFaceUrl());
                httpPost.setHeader("Content-Type", "application/json; charset=utf-8");
                httpPost.setEntity(entity0);
                response = httpClient.execute(httpPost);
                break;
            case "PUT":
                HttpPut httpPut = new HttpPut("http://" + host0 + face.getFaceUrl());
                httpPut.setHeader("Content-Type", "application/json; charset=utf-8");
                httpPut.setEntity(entity0);
                response = httpClient.execute(httpPut);
                break;
            case "GET":
                HttpGet httpGet = new HttpGet("http://" + host0 + face.getFaceUrl());
                httpGet.setHeader("Content-Type", "application/json; charset=utf-8");
                response = httpClient.execute(httpGet);
                break;
            case "DELETE":
                HttpDelete httpDelete = new HttpDelete("http://" + host0 + face.getFaceUrl());
                httpDelete.setHeader("Content-Type", "application/json; charset=utf-8");
                response = httpClient.execute(httpDelete);
                break;
            default:
                System.out.println("请求方法不存在，请确认后重试！");
                break;
            }

            responseEntity = response.getEntity();
            responseBody = EntityUtils.toString(responseEntity, "UTF-8");

            faceExecuteLogStatistics(faceId, authenticationId, moduleId, auth, body0, responseBody);

            responseBody = jsonFormat(responseBody);

            return responseBody;
            
        case "bearer_token":
            
            String host1 = getEnvConfigHost(face);

            String body1 = getGlobalParameter(face);

            StringEntity entity1 = setHttpEntity(body1);

            switch (face.getFaceMethod()) {
            case "POST":
                HttpPost httpPost = new HttpPost("http://" + host1 + face.getFaceUrl());
                httpPost.setHeader("Content-Type", "application/json; charset=utf-8");
                httpPost.setHeader("Authorization", auth.getToken());
                httpPost.setEntity(entity1);
                response = httpClient.execute(httpPost);
                break;
            case "PUT":
                HttpPut httpPut = new HttpPut("http://" + host1 + face.getFaceUrl());
                httpPut.setHeader("Content-Type", "application/json; charset=utf-8");
                httpPut.setHeader("Authorization", auth.getToken());
                httpPut.setEntity(entity1);
                response = httpClient.execute(httpPut);
                break;
            case "GET":
                HttpGet httpGet = new HttpGet("http://" + host1 + face.getFaceUrl());
                httpGet.setHeader("Content-Type", "application/json; charset=utf-8");
                httpGet.setHeader("Authorization", auth.getToken());
                response = httpClient.execute(httpGet);
                break;
            case "DELETE":
                HttpDelete httpDelete = new HttpDelete("http://" + host1 + face.getFaceUrl());
                httpDelete.setHeader("Content-Type", "application/json; charset=utf-8");
                httpDelete.setHeader("Authorization", auth.getToken());
                response = httpClient.execute(httpDelete);
                break;
            default:
                System.out.println("请求方法不存在，请确认后重试！");
                break;
            }

            responseEntity = response.getEntity();
            responseBody = EntityUtils.toString(responseEntity, "UTF-8");

            // 统计接口执行的日志
            faceExecuteLogStatistics(faceId, authenticationId, moduleId, auth, body1, responseBody);
            
            // 格式化返回的jSON数据
            responseBody = jsonFormat(responseBody);

            return responseBody;
        case "basic_auth":

            CredentialsProvider provider = new BasicCredentialsProvider();
            UsernamePasswordCredentials credentials = new UsernamePasswordCredentials(auth.getUsername(), auth.getPassword());
            provider.setCredentials(AuthScope.ANY, credentials);
            CloseableHttpClient basicHttpClient = HttpClients.custom().setDefaultCredentialsProvider(provider).build();
            
            String host2 = getEnvConfigHost(face);

            String body2 = getGlobalParameter(face);

            StringEntity entity2 = setHttpEntity(body2);

            switch (face.getFaceMethod()) {
            case "POST":
                HttpPost httpPost = new HttpPost("http://" + host2 + face.getFaceUrl());
                httpPost.setHeader("Content-Type", "application/json; charset=utf-8");
                httpPost.setEntity(entity2);
                response = basicHttpClient.execute(httpPost);
                break;
            case "PUT":
                HttpPut httpPut = new HttpPut("http://" + host2 + face.getFaceUrl());
                httpPut.setHeader("Content-Type", "application/json; charset=utf-8");
                httpPut.setEntity(entity2);
                response = basicHttpClient.execute(httpPut);
                break;
            case "GET":
                HttpGet httpGet = new HttpGet("http://" + host2 + face.getFaceUrl());
                httpGet.setHeader("Content-Type", "application/json; charset=utf-8");
                response = basicHttpClient.execute(httpGet);
                break;
            case "DELETE":
                HttpDelete httpDelete = new HttpDelete("http://" + host2 + face.getFaceUrl());
                httpDelete.setHeader("Content-Type", "application/json; charset=utf-8");
                response = basicHttpClient.execute(httpDelete);
                break;
            default:
                System.out.println("请求方法不存在，请确认后重试！");
                break;
            }

            responseEntity = response.getEntity();
            responseBody = EntityUtils.toString(responseEntity, "UTF-8");

            faceExecuteLogStatistics(faceId, authenticationId, moduleId, auth, body2, responseBody);

            responseBody = jsonFormat(responseBody);

            return responseBody;
        case "oauth_authorization_code":
            System.out.println("暂不开放此权限");
            break;
        case "oauth_implicit":
            System.out.println("暂不开放此权限");
            break;
        case "oauth_password_credentials":
            break;
        case "oauth_client_credentials":
            String access_token = FaceExecuteController.class.newInstance()
                    .permissionAuthentication(auth.getAccessTokenUrl(), auth.getUsername(), auth.getPassword());

            String host = getEnvConfigHost(face);

            String body = getGlobalParameter(face);

            StringEntity entity = setHttpEntity(body);

            switch (face.getFaceMethod()) {
            case "POST":
                HttpPost httpPost = new HttpPost("http://" + host + face.getFaceUrl());
                httpPost.setHeader("Content-Type", "application/json; charset=utf-8");
                httpPost.setHeader("Authorization", access_token);
                httpPost.setEntity(entity);
                response = httpClient.execute(httpPost);
                break;
            case "PUT":
                HttpPut httpPut = new HttpPut("http://" + host + face.getFaceUrl());
                httpPut.setHeader("Content-Type", "application/json; charset=utf-8");
                httpPut.setHeader("Authorization", access_token);
                httpPut.setEntity(entity);
                response = httpClient.execute(httpPut);
                break;
            case "GET":
                HttpGet httpGet = new HttpGet("http://" + host + face.getFaceUrl());
                httpGet.setHeader("Content-Type", "application/json; charset=utf-8");
                httpGet.setHeader("Authorization", access_token);
                response = httpClient.execute(httpGet);
                break;
            case "DELETE":
                HttpDelete httpDelete = new HttpDelete("http://" + host + face.getFaceUrl());
                httpDelete.setHeader("Content-Type", "application/json; charset=utf-8");
                httpDelete.setHeader("Authorization", access_token);
                response = httpClient.execute(httpDelete);
                break;
            default:
                System.out.println("请求方法不存在，请确认后重试！");
                break;
            }

            responseEntity = response.getEntity();
            responseBody = EntityUtils.toString(responseEntity, "UTF-8");

            faceExecuteLogStatistics(faceId, authenticationId, moduleId, auth, body, responseBody);

            responseBody = jsonFormat(responseBody);

            return responseBody;
        default:
            System.out.println("没有与之匹配的权限");
            break;
        }
        return "";

    }

    /**
     * @param responseBody
     * @return
     */
    private String jsonFormat(String responseBody) {
        // JSON格式化
        ObjectMapper mapper = new ObjectMapper();
        try {
            Object obj = mapper.readValue(responseBody, Object.class);
            responseBody = mapper.writerWithDefaultPrettyPrinter().writeValueAsString(obj);
        } catch (IOException e) {
            e.printStackTrace();
        }
        return responseBody;
    }

    /**
     * @param faceId
     * @param authenticationId
     * @param moduleId
     * @param auth
     * @param body
     * @param responseBody
     */
    private void faceExecuteLogStatistics(Integer faceId, Integer authenticationId, Integer moduleId,
            PermissionAuthentication auth, String body, String responseBody) {
        // 接口执行日志统计
        faceExecuteLog.setFaceId(faceId);
        faceExecuteLog.setModuleId(moduleId);
        faceExecuteLog.setAuthenticationId(authenticationId);
        faceExecuteLog.setAuthenticationType(auth.getAuthenticationType());
        faceExecuteLog.setRequestBody(body);
        faceExecuteLog.setResponseBody(responseBody);
        faceExecuteLog.setCreateTime(new Date());

        statisticService.addFaceExecutelog(faceExecuteLog);
    }

    /**
     * @param body
     * @return
     */
    private StringEntity setHttpEntity(String body) {
        StringEntity entity = new StringEntity(body, Charset.forName("UTF-8"));
        entity.setContentEncoding("UTF-8");
        entity.setContentType("application/json");
        return entity;
    }

    /**
     * @param face
     * @return
     */
    private String getGlobalParameter(FaceInfo face) {
        // 全局参数调用开始
        GlobalParameter globalParameter = globalParameterService.getGlobalParameter();

        String paramBody = globalParameter.getParamBody();
        String body = face.getFaceBody().trim();
        if (paramBody != null) {

            // JSON转Map
            JSONObject parseObject = JSON.parseObject(paramBody);
            Iterator<String> it = parseObject.keySet().iterator();

            while (it.hasNext()) {
                String key = it.next().toString();
                String value = parseObject.get(key).toString();

                // 获取全局变量在请求体中引用了几次
                int fromIndex = 0;
                int count = 0;
                while (true) {
                    int index = body.indexOf("${" + key + "}", fromIndex);
                    if (-1 != index) {
                        fromIndex = index + 1;
                        count++;
                    } else {
                        break;
                    }
                }

                // 拼接全局变量
                for (int i = 0; i < count; i++) {
                    int indexOf = body.indexOf("${" + key + "}");
                    if (indexOf != -1) {
                        String before = body.substring(0, indexOf);
                        String after = body.substring(indexOf + key.length() + 3, body.length());
                        body = before + value + after;
                    }
                }

            }

        }

        // 建立系统变量Map
        Map<String, Object> sysVarMap = new HashMap<String, Object>();

        sysVarMap.put("_uuid", UUID.randomUUID());

        SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'");
        df.setTimeZone(TimeZone.getTimeZone("UTC"));
        // UTC 0时区时间
        sysVarMap.put("_current_time_utc", df.format(new Date()));

        df.setTimeZone(TimeZone.getDefault());
        // UTC格式的当前系统的默认时区时间，服务器在北京获取的就是UTC+8的时间
        sysVarMap.put("_current_time_utc_local", df.format(new Date()));

        // 本地日期+时间
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        sysVarMap.put("_current_datetime", sdf.format(new Date()));

        // 本地日期
        SimpleDateFormat sdf1 = new SimpleDateFormat("yyyy-MM-dd");
        sysVarMap.put("_current_date", sdf1.format(new Date()));

        // 随机数字,从零到一亿之间的随机数字
        sysVarMap.put("_random_number", new Random(666).nextInt(100000000));

        Iterator<String> iterator = sysVarMap.keySet().iterator();

        while (iterator.hasNext()) {
            String key = iterator.next().toString();
            String value = sysVarMap.get(key).toString();

            // 获取系统变量在请求体中引用了几次
            int fromIndex = 0;
            int count = 0;
            for (int x = 0; x < sysVarMap.size(); x++) {
                while (true) {
                    int index = body.indexOf("${" + key + "}", fromIndex);
                    if (-1 != index) {
                        fromIndex = index + 1;
                        count++;
                    } else {
                        break;
                    }
                }
            }

            // 拼接系统变量
            for (int i = 0; i < count; i++) {
                int indexOf = body.indexOf("${" + key + "}");
                if (indexOf != -1) {
                    String before = body.substring(0, indexOf);
                    String after = body.substring(indexOf + key.length() + 3, body.length());
                    body = before + sysVarMap.get(key).toString() + after;
                }
            }

        }
        return body;
    }

    /**
     * @param face
     * @return
     */
    private String getEnvConfigHost(FaceInfo face) {
        // 根据FaceHost查询对应的IP:端口或者域名:端口
        EnvConfig envConfig = envConfigService.getEnvConfig();
        String envConfigParamBody = envConfig.getParamBody();

        Map<String, Object> map = new HashMap<String, Object>();
        if (envConfigParamBody != null) {
            map = JSON.parseObject(envConfigParamBody);
        }

        String host = null;

        for (String key : map.keySet()) {
            if (key.equalsIgnoreCase(face.getFaceHost())) {
                host = map.get(key).toString();
            }
        }
        return host;
    }
}
